﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Draco.OWF.Exceptions;
using Draco.OWF.RT;

namespace Draco.OWF.Process
{
    public abstract class AbstractProcess:IProcess
    {
        protected String m_Id = Guid.NewGuid().ToString();
        protected String m_DisplayName = "";
        protected List<IActivity> m_Activities = new List<IActivity>();
        protected List<IRoute> m_Routes = new List<IRoute>();
        protected IActivity m_StartActivity;
        protected IActivity m_EndActivity;

        public AbstractProcess():this("","") { }
        public AbstractProcess(String name):this("",name){ }
        public AbstractProcess(String id,String name) 
        {
            if (String.IsNullOrEmpty(name))
                name = "untitled workflow";
            this.Id = id;
            this.Name = name;
            this.CreateDate = DateTime.Now;
        }
        public String Creator { get; set; }
        public DateTime CreateDate { get; set; }
        public String Version { get; set; }
        /// <summary>
        /// Id
        /// </summary>
        public virtual string Id
        {
            get{return m_Id;}
            set
            {
                if (!String.IsNullOrEmpty(value))
                    m_Id = value;
            }
        }
        /// <summary>
        /// Name
        /// </summary>
        public virtual String Name { get; set; }
        /// <summary>
        /// DisplayName
        /// </summary>
        public virtual String DisplayName 
        { 
            get {return String.IsNullOrEmpty(m_DisplayName) ? Name : m_DisplayName; } 
            set { m_DisplayName = value; } 
        }
        /// <summary>
        /// Description
        /// </summary>
        public virtual String Description { get; set; }
        /// <summary>
        /// Readonly
        /// </summary>
        public bool Readonly { get; set; }

        public virtual IActivity StartActivity
        {
            get { return m_StartActivity; }
            set { m_StartActivity = value; }
        }

        public virtual IActivity EndActivity
        {
            get { return m_EndActivity; }
            set { m_EndActivity = value; }
        }

        public virtual List<IActivity> Activities
        {
            get { return m_Activities.ToList(); }
        }

        public virtual List<IRoute> Routes
        {
            get { return m_Routes.ToList(); }
        }

        public virtual IActivity GetActivity(string activityId)
        {
            if (this.m_Activities.Count > 0)
            {
                foreach (IActivity ac in m_Activities)
                {
                    if (ac.Id == activityId)
                        return ac;
                }
            }
            return null;
        }

        public virtual IRoute GetRoute(string routeId)
        {
            if (this.m_Routes.Count > 0)
            {
                foreach (IRoute route in this.m_Routes)
                {
                    if (route.Id == routeId)
                    {
                        return route;
                    }
                }
            }
            return null;
        }
        public virtual IRoute GetRoute(string fromAcId, string toAcId)
        {
            if (this.m_Routes.Count > 0)
            {
                foreach (IRoute route in this.m_Routes)
                {
                    if (route.FromId == fromAcId && route.ToId == toAcId)
                    {
                        return route;
                    }
                }
            }
            return null;
        }
        
        public virtual List<IRoute> GetFromActivityRoutes(string activityId)
        {
            List<IRoute> list = new List<IRoute>();
            if (this.m_Routes.Count > 0)
            {
                foreach (IRoute route in this.m_Routes)
                {
                    if (route.FromId == activityId)
                    {
                        list.Add(route);
                    }
                }
            }
            return list;
        }

        public virtual List<IRoute> GetToActivityRoutes(string activityId)
        {
            List<IRoute> list = new List<IRoute>();
            if (this.m_Routes.Count > 0)
            {
                foreach (IRoute route in this.m_Routes)
                {
                    if (route.ToId == activityId)
                    {
                        list.Add(route);
                    }
                }
            }
            return list;
        }
        
        public virtual List<IActivity> GetNextActivities(string activityId)
        {
            List<IActivity> list = new List<IActivity>();
            List<IRoute> routes = GetFromActivityRoutes(activityId);
            foreach (IRoute route in routes)
            {
                IActivity ac = this.GetActivity(route.ToId);
                list.Add(ac);
            }
            return list;
        }

        public virtual List<IActivity> GetPreviousActivities(string activityId)
        {
            List<IActivity> list = new List<IActivity>();
            List<IRoute> routes = GetToActivityRoutes(activityId);
            foreach (IRoute route in routes)
            {
                IActivity ac = this.GetActivity(route.FromId);
                list.Add(ac);
            }
            return list;
        }

        public virtual void AddActivity(IActivity activity)
        {
            if (activity == null)
                throw new ArgumentNullException("Argument [activity] is null");
            if(this.Readonly)
                throw new WorkflowReadonlyException();
            IActivity ac = this.GetActivity(activity.Id);
            if (ac != null)
            {
                throw new WorkflowException("Activity Exist,activity Id:" + activity.Id);
            }
            this.m_Activities.Add(activity);
        }

        public virtual void AddRoute(IRoute route)
        {
            if (route==null)
                throw new ArgumentNullException("Argument [route] is null");
            if (this.Readonly)
                throw new WorkflowReadonlyException();
            IRoute r = this.GetRoute(route.Id);
            if (r != null)
            {
                throw new WorkflowException("Route Exist,route Id:" + route.Id);
            }
            this.m_Routes.Add(route);
        }

        public virtual void RemoveActivity(string activityId)
        {
            IActivity ac = this.GetActivity(activityId);
            if (ac != null)
                this.m_Activities.Remove(ac);
        }

        public virtual void RemoveRoute(string routeId)
        {
            IRoute route = this.GetRoute(routeId);
            if (route != null)
                this.m_Routes.Remove(route);
        }

        public virtual void Clear()
        {
            this.m_Activities.Clear();
            this.m_Routes.Clear();
        }

        public virtual bool Check()
        {
            if (this.m_Activities.Count ==0 && this.m_Routes.Count == 0)
                return true;    //empty
            if (this.StartActivity == null || this.EndActivity == null)
                return false;   // no start entry
            if (this.m_Activities.Count == 1 && this.m_Routes.Count == 0)
                return true; //only one activity
            foreach (IRoute route in this.m_Routes)
            {
                IActivity acFrom = this.GetActivity(route.FromId);
                IActivity acTo = this.GetActivity(route.ToId);
                if (acFrom == null || acTo == null)
                    return false;   //Invalid route
            }
            foreach (IActivity activity in this.m_Activities)
            {
                List<IRoute> from = this.GetFromActivityRoutes(activity.Id);
                List<IRoute> to = this.GetToActivityRoutes(activity.Id);
                if (from.Count == 0 && to.Count == 0) //Unreachable activity
                    return false;
                if (from.Count == 0 && activity.Id != this.EndActivity.Id)
                    return false;
                if (to.Count == 0 && activity.Id != this.StartActivity.Id)
                    return false;
            }
            return true;
        }

        public virtual bool EnsureStartActivity()
        {
            if (this.m_StartActivity != null)
                return true;
            foreach (IActivity activity in this.m_Activities)
            {
                List<IRoute> to = this.GetToActivityRoutes(activity.Id);
                if (to.Count == 0) //start activity
                {
                    this.m_StartActivity = activity;
                    return true;
                }
            }
            return false;
        }

        public abstract IJob CreateProcessInstance(DataBinding.IBindingDataItemCollection startParameters);

    }
}
